Script-as-Pipeline — Agent 管线中的脚本节点设计模式

你的 agent 跑完脚本之后,stdout 去哪了?

你写了一个 skill,里面有个 shell 脚本。Agent 执行它,读取输出,然后继续工作。看起来没什么特别的——脚本就是个工具,agent 用它干活。

但仔细想一下:脚本的 stdout 被注入到了 agent 的上下文中。它和你的 system prompt、上一步的输出、用户的指令混在一起,成为 agent 下一步决策的依据。

这可不是"工具返回了一个结果"那么简单。脚本的 stdout 是一段动态生成的 prompt 片段。 它可以在 agent 不知道的情况下,改变它的行为路径。

这意味着什么?意味着 script 不是 agent 的工具——script 是 agent 管线中的一个节点,它拥有和 prompt 同等的"编程"能力。

脚本的三层角色

业界对 script 在 agent 系统中的用法,大致可以分成三个层级。大多数团队停在第一层。

第一层:Guardrail(门卫)

脚本只做一件事:检查,然后返回 exit code。0 继续,非 0 停下。

这是 Anthropic "Building Effective Agents" 博客中描述的 Prompt Chaining 模式——在 LLM 步骤之间插入确定性检查,防止错误在链上传播。LangGraph 的 ToolNode 本质上也是这一层:tool 返回值写进 state,但路由逻辑写在 conditional edge 里,和 tool 本身是分离的。

这一层已经普及了。但它把脚本当成一个"门"——只能决定过不过,不能决定往哪走。

第二层:Informant(导航员)

脚本不仅返回状态,还返回结构化的行动信号。Agent 不是简单地"看到输出",而是解析语义标记后决定行为。

一个具体的例子:我在设计 meta-harness 时,给所有脚本定义了三类 action token:

这不是给人看的日志。这是给 agent 解析的语义标记——脚本在说"接下来你要做这个",而不是"我执行完了,这是结果"。

更关键的是,stdout 里的数据可以直接路由管线。scaffold.sh 输出 created:5 / skipped:3——当 skipped > 0 时,agent 自动激活 archive comparison 分支,对比新旧文件差异。这不是 agent 自己"想到"的,是脚本的输出引导它走的。

第三层:Conductor(指挥官)

这是我在形式化的方向:脚本不只是输出状态标记,而是输出下一段 prompt 的完整片段——包括路由指令、约束条件、甚至临时的行为规则。

管线的形状变成:

step1 → script0 → stdout 输出路由标记
                     ├─ 路径A → script1a → step2a
                     └─ 路径B → script1b → step2b

控制流从 prompt 的自然语言描述,下沉到了脚本的确定性逻辑。Prompt 仍然重要,但它不再负责"流程控制"——它负责理解上下文和生成输出,脚本负责管线的路由。

为什么是 stdout?

Agent 和脚本之间的通信有几种候选通道:exit code、文件系统、数据库、网络。为什么是 stdout?

因为 stdout 有一个其他通道没有的特性:它被自动注入到 agent 的上下文中。 不需要 agent 主动去读文件、不需要额外的 tool call、不需要轮询。脚本执行完毕,输出就在 agent 的"视野"里了。

这听起来像是技术细节,但它的意义远超技术细节。Unix 管道能工作,是因为每个程序遵守同一个契约:stdin 是文本流,stdout 也是文本流,\n 分隔。这个极简的契约让任意程序可以组合——前一个的输出直接变成后一个的输入。

Agent 管线缺的就是这一层。Tool result 注入上下文时,没有任何格式约定——agent 自由解释,路由完全依赖 prompt 中的文字指令。当管线变复杂时,prompt 里的 if/else 越来越长,agent 的理解误差越来越大。

Action token 体系就是这个契约的雏形。它的设计约束很明确:

和文件式通信的边界

这里有必要区分两种模式,因为它们容易混淆。

文件式 Agent 间通信——比如 Anthropic 的 Planner → Generator → Evaluator 通过 spec/sprint contract 文件交接——是异步的、完整的上下文传递。一个 agent 写完文件退出,另一个 agent 启动时读取。目标是信息不丢失。

Script stdout 是同步的、增量的上下文注入。Agent 不停下,脚本作为一个管线节点被嵌入到 agent 的执行流中。目标是控制流路由。

两者不是替代关系,而是互补。文件适合 agent 之间,stdout 适合 agent 与 harness 组件之间。

脚本作为管线节点的真正意义

把 script 从"工具"升级为"管线节点",改变的不只是实现方式,而是设计 agent 系统时的思考框架。

当你把 script 看作工具时,你在想"agent 需要什么功能,我写个脚本给它用"。这是以 agent 为中心的视角。

当你把 script 看作管线节点时,你在想"这条管线上有哪些决策点、哪些验证点、哪些状态需要传递"。这是以系统为中心的视角。

前者是"用 agent",后者是"设计 agent 系统"。两条路的能力天花板不同。

这直接关联到 Harness Engineering 的核心主张:agent 的有效性不来自它的 system prompt,而来自围绕它的环境结构。Script stdout 是环境向 agent "说话"的方式——不是通过人类预先写好的 prompt,而是通过运行时动态生成的信息。

参考